/**
 * This file is a part of qloudgen-sec. 
 * You can redistribute qloudgen-sec and/or modify it under the terms of the Lesser GNU General Public License version 3. 
 * qloudgen-sec is distributed WITHOUT ANY WARRANTY. 
 *
 * See the Lesser GNU General Public License for more details at http://www.gnu.org/licenses/. 
 **/
package org.qloudgen.sec.shiro.permission;

import java.util.List;
import java.util.ArrayList;

import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.AuthorizationException;

import org.qloudgen.rul.drools.Message;
import org.qloudgen.rul.drools.rule.DroolsRuleRunner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>Title: Drools Permission Implementation</p>
 * <p>Description: Wrappered jboss drools rule engine as permission implementation.</p>
 * <p>Copyright: qloudgen/sec 2014</p>
 * @author <a href="mailto:hyq.dd.sun@gmail.com">glue.3M</a>
 */
public final class DroolsPermission implements Permission {
	/**
	 * Log output.
	 */
	private final static Logger logger = LoggerFactory.getLogger( DroolsPermission.class );
	/**
	 *
	 */
	private DroolsRuleRunner droolsRuleRunner = null;
	/**
	 *
	 */
	private String name = null;
	/**
	 *
	 */
	private Object[] facts = null;
	
	/**
	 * Default constructor.
	 */
	public DroolsPermission() {
	}
	
	/**
	 * Custom constructor.
	 *
	 * @param name
	 * @param facts
	 */
	public DroolsPermission(String name , Object[] facts) {
		this( null , name , facts );
	}
	
	/**
	 * Custom constructor.
	 *
	 * @param droolsRuleRunner
	 * @param facts
	 */
	public DroolsPermission(DroolsRuleRunner droolsRuleRunner , Object[] facts) {
		this( droolsRuleRunner , null , facts );
	}
	
	/**
	 * Custom constructor.
	 *
	 * @param droolsRuleRunner
	 * @param name
	 * @param facts
	 */
	public DroolsPermission(DroolsRuleRunner droolsRuleRunner , String name , Object[] facts) {
		this.droolsRuleRunner = droolsRuleRunner;
		this.name = name;
		this.facts = facts;
	}

	/**
	 * Sets current drool rule runner.
	 *
	 * @param droolsRuleRunner
	 */
	public void setDroolsRuleRunner(final DroolsRuleRunner droolsRuleRunner) {
		this.droolsRuleRunner = droolsRuleRunner;
	}
	
	/**
	 * Returns current drool rule runner.
	 *
	 * @return DroolsRuleRunner
	 */
	public DroolsRuleRunner getDroolsRuleRunner() {
		return droolsRuleRunner;
	}
	
	/**
	 * Sets current drool session name.
	 *
	 * @param name
	 */
	public void setName(final String name) {
		this.name = name;
	}
	
	/**
	 * Returns current drool session name.
	 *
	 * @return String
	 */
	public String getName() {
		return name;
	}

	/**
	 * Sets current drool rule facts.
	 *
	 * @param facts
	 */
	public void setFacts(final Object[] facts) {
		this.facts = facts;
	}
	
	/**
	 * Returns current drool facts.
	 *
	 * @return Object[]
	 */
	public Object[] getFacts() {
		return facts;
	}
	
	/**
	 * Returns current authorization result.
	 *
	 * @return boolean
	 */
	public boolean implies(Permission target) {
		//
		if ( ! ( target instanceof DroolsPermission ) ) {
			throw new AuthorizationException( "Unknown permission type..." );
		}
		/**
		 * Do merage all facts.
		 */
		Object[] ruleFacts = merageFacts( ( ( DroolsPermission )target ).getFacts() );
		/**
		 * Do run drools engine.
		 */
		droolsRuleRunner.runRules( ( ( DroolsPermission )target ).getName() , ruleFacts );
		/**
		 * Do get rule result.
		 */
		return isPermitted( ruleFacts );
	}
	
	/**
	 * Merage current rule facts.
	 *
	 * @param targetFacts
	 * @return Object[]
	 */
	private Object[] merageFacts(final Object[] targetFacts) {
		/**
		 * Arrays.asList will return fixed-size list, Can't be add!
		 */
		List<Object> result = new ArrayList<Object>();
		//
		if ( facts != null ) {
			for ( int i = 0; i < facts.length; i++ ) {
				result.add( facts[i] );
			}
		}
		//
		if ( targetFacts != null ) {
			for ( int j = 0; j < targetFacts.length; j++ ) {
				result.add( targetFacts[j] );
			}
		}
		//
		return result.toArray();
	}
	
	/**
	 * Returns current permitted.
	 *
	 * @param facts
	 * @return boolean
	 */
	private boolean isPermitted(final Object[] facts) {
		//
		dump( facts );
		//
		if ( facts.length > 0 ) {
			if ( facts[0] instanceof Message ) {
				return ( ( Message )facts[0] ).isPermitted();
			}
		}
		//
		return false;
	}
	
	/**
	 *
	 *
	 * @param facts
	 */
	private void dump(final Object[] facts) {
		if ( logger.isTraceEnabled() ) {
			for ( int i = 0; i < facts.length; i++ ) {
				logger.trace( "Current return fact object...{}" , facts[i] );
			}
		}
	}
}